﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Windows;
using System.Reflection.Emit;
using System.Windows.Input;

namespace RIDA.Globals
{
    public class CompositeCommandParameter
    {
        public object Sender { get; set; }
        public object Parameter { get; set; }
    }
    public class AttachedEventBehavior
    {
        public static readonly DependencyProperty EventProperty = DependencyProperty.RegisterAttached("Event", typeof(string), typeof(AttachedEventBehavior), new UIPropertyMetadata(null, OnEventChanged));
        public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(AttachedEventBehavior), new UIPropertyMetadata(null, OnCommandChanged));
        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(AttachedEventBehavior), new UIPropertyMetadata(null, OnCommandParameterChanged));
        public static readonly DependencyProperty UseCompositionProperty = DependencyProperty.RegisterAttached("UseComposition", typeof(string), typeof(AttachedEventBehavior), new UIPropertyMetadata("False", OnUseCompositionChanged));

        #region Getters/Setters
        public static string GetEvent(DependencyObject parameter)
        {
            return (string)parameter.GetValue(EventProperty);
        }

        public static void SetEvent(DependencyObject parameter, string value)
        {
            parameter.SetValue(EventProperty, value);
        }
        public static ICommand GetCommand(DependencyObject parameter)
        {
            return (ICommand)parameter.GetValue(CommandProperty);
        }

        public static void SetCommand(DependencyObject parameter, ICommand value)
        {
            parameter.SetValue(CommandProperty, value);
        }
        public static object GetCommandParameter(DependencyObject parameter)
        {
            return parameter.GetValue(CommandParameterProperty);
        }

        public static void SetCommandParameter(DependencyObject parameter, object value)
        {
            parameter.SetValue(CommandParameterProperty, value);
        }

        public static string GetUseComposition(DependencyObject parameter)
        {
            return (string)parameter.GetValue(UseCompositionProperty);
        }

        public static void SetUseComposition(DependencyObject parameter, string value)
        {
            parameter.SetValue(UseCompositionProperty, value);
        }
        #endregion


        static void OnEventChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
        {
            if (e.OldValue == null)
            {
                EmitDynamicEventHandler(depObj);
            }
        }

        static void OnCommandChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) { }
        static void OnCommandParameterChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) { }
        static void OnUseCompositionChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="depObj"></param>
        /// <param name="args"></param>
        public static void ExecuteDynamicEventHandler(DependencyObject depObj, object args)
        {
            var command = GetCommand(depObj);
            object commandParameter = null;
            if (GetUseComposition(depObj) == "True")
            {
                commandParameter = new CompositeCommandParameter()
                {
                    Sender = depObj,
                    Parameter = args
                };
            }
            else
            {
                commandParameter = GetCommandParameter(depObj);
            }

            if (command.CanExecute(commandParameter))
            {
                command.Execute(commandParameter);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="depObj"></param>
        private static void EmitDynamicEventHandler(DependencyObject depObj)
        {
            var eventName = GetEvent(depObj);
            var eventInfo = depObj.GetType().GetEvent(eventName);

            Type handlerType = eventInfo.EventHandlerType;
            MethodInfo invokeMethod = handlerType.GetMethod("Invoke");
            ParameterInfo[] parms = invokeMethod.GetParameters();
            Type[] parmTypes = new Type[parms.Length];
            for (int i = 0; i < parms.Length; i++)
            {
                parmTypes[i] = parms[i].ParameterType;
            }
            DynamicMethod handler = new DynamicMethod("DynamicHandler", invokeMethod.ReturnType, parmTypes, true);
            ILGenerator il = handler.GetILGenerator();
            //local vars
            il.DeclareLocal(typeof(DependencyObject));
            il.DeclareLocal(typeof(object));
            //proc the parameters
            il.Emit(OpCodes.Ldarg_0);//object. 
            il.Emit(OpCodes.Castclass, typeof(DependencyObject));//need casting to DependencyObject
            il.Emit(OpCodes.Stloc_0);
            il.Emit(OpCodes.Ldloc_0);//store in loc var

            il.Emit(OpCodes.Ldarg_1);//eventargs. 
            il.Emit(OpCodes.Stloc_1);
            il.Emit(OpCodes.Ldloc_1);//store in loc var
            il.Emit(OpCodes.Call, typeof(AttachedEventBehavior).GetMethod("ExecuteDynamicEventHandler", new Type[] { typeof(DependencyObject), typeof(object) }));
            il.Emit(OpCodes.Ret);

            Delegate d = handler.CreateDelegate(handlerType);
            eventInfo.AddEventHandler(depObj, d);
        }
       



    }
}
